/*
 * Copyright 2007-2008 Volker Fritzsch
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */
package motej;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.swing.event.EventListenerList;

import motej.centore.NewLogger;
import motej.centore.RotationController;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.intel.bluetooth.BlueCoveConfigProperties;
import com.intel.bluetooth.BlueCoveImpl;

/**
 * 
 * <p>
 * @author <a href="mailto:vfritzsch@users.sourceforge.net">Volker Fritzsch</a>
 * @author Daniel Centore
 */
public class MoteFinder
{

	// initialization on demand holder idiom
	private static class SingletonHolder
	{

		private static final MoteFinder INSTANCE = new MoteFinder();

	}

	/**
	 * Returns the <code>WiimoteFinder</code> instance.
	 * 
	 * @return WiimoteFinder
	 */
	public static MoteFinder getMoteFinder()
	{
		try
		{
			// disable PSM minimum flag because the wiimote has a PSM below 0x1001
			System.setProperty(BlueCoveConfigProperties.PROPERTY_JSR_82_PSM_MINIMUM_OFF, "true");

			SingletonHolder.INSTANCE.currentID = RotationController.setNextDongle();

			SingletonHolder.INSTANCE.localDevice = LocalDevice.getLocalDevice();

			SingletonHolder.INSTANCE.discoveryAgent = SingletonHolder.INSTANCE.localDevice.getDiscoveryAgent();

			return SingletonHolder.INSTANCE;
		} catch (BluetoothStateException ex)
		{
			throw new RuntimeException(ex);
		}
	}

	private Logger log = LoggerFactory.getLogger(MoteFinder.class);
	private EventListenerList listenerList = new EventListenerList();
	private DiscoveryAgent discoveryAgent;
	protected Set<String> bluetoothAddressCache = new HashSet<String>();

	private Object currentID = null;

	protected final DiscoveryListener listener = new DiscoveryListener()
	{
		public synchronized void deviceDiscovered(final RemoteDevice device, DeviceClass clazz)
		{
			try
			{
				NewLogger.println("Device discovered: [" + device.getFriendlyName(true) + "] [" + device.getBluetoothAddress() + "]");
			} catch (IOException e)
			{
			}

			try
			{
				if (!device.getFriendlyName(true).startsWith("Nintendo"))
				{ //("Nintendo RVL-CNT-01") != 0) {
					return;
				}
			} catch (IOException ex)
			{
				NewLogger.println("Could not get friendly name. Probably not a WiiMote.");
			}

			final String address = device.getBluetoothAddress();

			if (!bluetoothAddressCache.contains(address))
			{
				Thread connectThread = new Thread("connect: " + address)
				{
					public void run()
					{
						Mote mote = new Mote(address, currentID, MoteFinder.this);

						//						RotationController.setNextDongle();
						currentID = RotationController.setNextDongle();

						bluetoothAddressCache.add(address);
						
						fireMoteFound(mote);
					};
				};
				connectThread.start();
			}
			else
				System.out.println("denied connection");
		}

		public void inquiryCompleted(int discType)
		{
			try
			{
				NewLogger.println("Restarting discovery... (Previous one finished)");
				MoteFinder.this.startDiscovery();
			} catch (BluetoothStateException e)
			{
			}
		}

		public void servicesDiscovered(int arg0, ServiceRecord[] arg1)
		{
			// unused
		}

		public void serviceSearchCompleted(int arg0, int arg1)
		{
			// unused
		}
	};

	private LocalDevice localDevice;

	private MoteFinder()
	{
	};

	public void addMoteFinderListener(MoteFinderListener listener)
	{
		listenerList.add(MoteFinderListener.class, listener);
	}

	protected void fireMoteFound(Mote mote)
	{
		MoteFinderListener[] listeners = listenerList.getListeners(MoteFinderListener.class);
		for (MoteFinderListener l : listeners)
		{
			l.moteFound(mote);
		}
	}

	public void removeMoteFinderListener(MoteFinderListener listener)
	{
		listenerList.remove(MoteFinderListener.class, listener);
	}

	public void startDiscovery() throws BluetoothStateException
	{
		RotationController.initializeDongles();

		try
		{
			discoveryAgent.startInquiry(DiscoveryAgent.GIAC, listener);
		} catch (BluetoothStateException ex)
		{
			throw new RuntimeException(ex);
		}
	}

	public void stopDiscovery()
	{
		discoveryAgent.cancelInquiry(listener);
	}

}
